package com.demo.core;

import com.demo.annotation.*;
import com.demo.aop.AopWrapper;
import com.demo.aop.AspectWrapper;
import com.demo.aop.ProxyConfig;
import com.demo.aop.ProxyFactory;
import com.demo.util.BeanUtils;
import com.demo.util.FileScanUtils;
import com.demo.util.StringUtils;
import java.lang.reflect.Field;
import java.util.*;

public class AnnotationApplicationContext implements ApplicationContext{
    private Map<String,Object> container = new HashMap<>();
    private List<String> beanNames = new ArrayList<>();
    private Set<BeanDefinition> definitions;
    private ProxyFactory proxyFactory;
    private boolean isProxy = false;

    public AnnotationApplicationContext(String basePackageName) {
        // 1，获得 该包下的所有带有注解的类
        definitions = this.findBeanDefinitions(basePackageName);
//        2，根据原材料创建bean 到容器中
        createBeans(definitions);
//        3，进行自动装载 auto wired
        autoWiredObject(definitions);
//        4,对bean进行后置处理，例如 把原来的对象替换为代理对象
        if(isProxy){ // 实例化 proxy工厂 添加这个标志位的原因是 可能没有必要 对对象进行代理处理
            proxyFactory = new ProxyFactory(definitions);
            this.proxyHandle();
        }

    }

    /**
     * 进行后置bean的处理，产生代理对象
     */
    public void proxyHandle(){
        Set<ProxyConfig> proxyConfigs = proxyFactory.getProxyConfigs(); // proxyConfigs 里面有
        //取出原始类及其额外方法
        proxyConfigs.forEach(proxyConfig -> {
            if(proxyConfig.getPointCutWrappers().size()>0){
                Object target = getBean(proxyConfig.getTarget());//真实对象
                List<AspectWrapper> aspectWrappers = new ArrayList<>();
                proxyConfig.getPointCutWrappers().forEach(x->{
                    aspectWrappers.add(new AspectWrapper(x,getBean(x.getExtName())));
                });
                AopWrapper aopWrapper = new AopWrapper(target,aspectWrappers);
                Object proxyObject = proxyFactory.createProxyObject(aopWrapper);
                container.replace(proxyConfig.getTarget(),proxyObject); //进行替换
            }
        });
    }


    /**
     * 进行对象的自动注入
     * @param definitions
     */
    public void autoWiredObject(Set<BeanDefinition> definitions){
        definitions.forEach(def->{
            Class clazz = def.getBeanClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                AutoWired autoWired = field.getAnnotation(AutoWired.class);
                if(autoWired!=null){
                    // 进行属性的注入
                    Qualifier qualifier = field.getAnnotation(Qualifier.class);
                    String injectClassName ;
                    if(qualifier==null){
                        //1，根据类型进行注入
                        String str = field.getType().getName();
                        injectClassName =StringUtils.initialLower(str.replaceAll(clazz.getPackageName()+".","")) ;
                    }else {
                        //2，根据id进行注入
                        injectClassName = qualifier.value();
                    }
//                    注入类型的不同只取决于 bean 的id
                    Object injectObject = getBean(injectClassName);
                    Object target = getBean(def.getBeanName());
                    try {
                        BeanUtils.objectAttributeAssign(target,target.getClass(),injectObject,field);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        });
    }
    /**
     * 根据原材料来创建bean
     * @param definitions
     */
    public void createBeans(Set<BeanDefinition> definitions){
        definitions.forEach(def->{
            String beanName = def.getBeanName();
            Class clazz = def.getBeanClass();
            try {
                // 实例化对象 （此时是一个空置对象）
                Object object = clazz.getDeclaredConstructor().newInstance();
                Field[] declaredFields = clazz.getDeclaredFields();
                //value注解，对基本类型进行初始化
                for (Field field : declaredFields) {
                    Value valueAnno = field.getAnnotation(Value.class);
                    if(valueAnno!=null){
                        String filedValue = valueAnno.value();
                        if (!"".equals(filedValue)){
                            BeanUtils.baseAttributeAssign(clazz,object,field,filedValue);
                        }
                    }

                }
                container.put(beanName,object);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    public Object getBean(String beanName){
        return container.get(beanName);
    }
    public Integer getBeanDefinitionCount(){
        return beanNames.size();
    }
    public String[] getBeanDefinitionNames(){
        return beanNames.toArray(new String[0]);
    }

    /**
     *
     * @param basePackageName 包名
     * @return 返回 bean定义的 封装类
     */
    public Set<BeanDefinition> findBeanDefinitions(String basePackageName){
        Set<Class<?>> classes = FileScanUtils.getClasses(basePackageName);
        Set<BeanDefinition> beanDefinitions = new HashSet<>();
        classes.forEach(x->{
            Component component = x.getAnnotation(Component.class);
            if(component!=null){
                if(!isProxy){ //如果遇到了aop增强注解表示可能要 开启注解代理工厂
                    Aspectj annotation = x.getAnnotation(Aspectj.class);
                    if(annotation!=null){
                        isProxy = true;
                    }
                }
                String beanName = component.value();//这是注解的id值
                if("".equals(beanName)){ //如果beanName是 “”的话
                    String className= x.getName().replaceAll(x.getPackageName()+".","");
                    beanName = StringUtils.initialLower(className);
                }
                beanDefinitions.add(new BeanDefinition(beanName,x));
                beanNames.add(beanName);
            }
        });
        return beanDefinitions;
    }

    public Map<String, Object> getContainer() {
        return container;
    }

    public void setContainer(Map<String, Object> container) {
        this.container = container;
    }

    public List<String> getBeanNames() {
        return beanNames;
    }

    public void setBeanNames(List<String> beanNames) {
        this.beanNames = beanNames;
    }

    public Set<BeanDefinition> getDefinitions() {
        return definitions;
    }

    public void setDefinitions(Set<BeanDefinition> definitions) {
        this.definitions = definitions;
    }

    public ProxyFactory getProxyFactory() {
        return proxyFactory;
    }

    public void setProxyFactory(ProxyFactory proxyFactory) {
        this.proxyFactory = proxyFactory;
    }
}
